// axios的二次封装
import axios from "axios";
// 进度条 引入js和css文件
import NProgress from "nprogress";
import "nprogress/nprogress.css";
// 进度条什么时候开始？ 什么时候结束？ 请求前（请求拦截） 请求后（响应拦截）
import store from "@/store";

// 去掉加载圈圈
NProgress.configure({ showSpinner: false });

// 错误信息对象
const errMessages = {
  401: "未授权",
  403: "禁止访问",
  404: "资源找不到",
  407: "token过期了",
  500: "服务器错误",
};
// 创建axios 实例
const request = axios.create({
  baseURL: "/api", // 设置公共路径前缀
  timeout: 20000, // 设置超时时间，如果超过时间，请求会中断没有响应
  // headers: {'X-Custom-Header': 'foobar'}
});


/*
token
 1.发送请求，携带用户唯一标识 token
 为什么要token？ 有些页面是需要用户登录了才可以进的 比如购物车页面
    而服务器就是通过判断请求参数是否有 token，来判断该请求的用户是否登录过。
    所以，我们发送请求需要携带 token 参数，这样服务器就能识别该用户了。
  怎么做？ 从本地储存中读取token或者从Vuex内存读取  然后在请求拦截器中设置请求头携带token
  2.解决 token 过期问题
  token 代表用户的唯一标识，它是有时效性的。
    因为如果没有时效性，万一泄漏，黑客是可以无限的操作你的账号，非常危险。
    有时效性，黑客最多也就操作 1 礼拜，危害相对较小。（同时也能通过重新登录来重置 token，解决这个问题）
  在响应拦截器中服务器响应失败中 根据响应状态码是407 判断token过期
  token过期后  1.发送请求告诉服务器token失效清除token(window.location.reload();刷新页面)  2.客户端也要清除掉储存在本地或Vuex中的token 

*/

// 请求拦截器
request.interceptors.request.use(
  // 在发送请求之前做些什么
  // config是请求配置对象
  (config) => {
    // 在请求拦截器中设置请求头携带token 以后向服务器发送请求都要携带token 服务器根据token辨识用户
    // localStorage是硬盘读取 , 永久储存    sessionStorage 是会话储存
    // 需要多处读取 在考虑性能上 采用store的token 是内存读取  
    // const token = JSON.parse( localStorage.getItem('user'))?.token 
    const token = store.state.user.token  
    if(token){
      config.headers.token = token // 如果有token 设置请求头参数token
    }
    // 开始进度条
    NProgress.start();
    return config; // 返回请求对象 不然请求都失败
  },
  (e) => {
    // 对请求错误做些什么
    return Promise.reject(e);
  }
);

// 响应拦截器
request.interceptors.response.use(
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么
  (response) => {
    // 结束进度条
    NProgress.done();
    // console.log(response);
    if (response.data.code === 200) {
      // 功能成功 返回成功的数据
      return response.data.data;
    } else {
      // code不是200 功能失败 提示错误信息 返回失败promise
      alert(response.data.message || "未知错误，请联系管理员解决");
      return Promise.reject();
    }
  },
  (e) => {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    // console.log(e);

    // 结束进度条
    NProgress.done();
    // 1.服务器有响应结果，但是失败
    if (e.response) {
      // 根据对应的响应状态码提示错误信息
      alert(errMessages[e.response.status] || "未知错误，请联系管理员解决");
      // token 过期失效处理
      if(e.response.status === 407){
        // 1.清除客户端储存的数据
        store.commit('user/RESET')
        // 相当于刷新页面 2.会重新发送请求告诉服务器token失效清除服务器端的token
        window.location.reload()
      }
    } else {
      // 2.服务器没有响应结果
      // 根据对应的err.message 提示失败原因 （1.网络超时 2.断网）
      if (e.message.indexOf("timeout") !== -1) {
        alert("请求超时,请检查网络再重试！");
      } else if (e.message.indexOf("Network") !== -1) {
        alert("断网,请检查网络再重试！");
      } else {
        alert("未知错误，请联系管理员解决");
      }
    }
    // 返回失败的promise
    return Promise.reject(e);
  }
);
// 暴露request
export default request;

/*
对axios进行二次封装，完成以下功能：
  1. 复用请求地址 /api
  2. 请求成功，得到成功数据
  3. 请求失败，自动提示失败原因
    请求失败
    功能失败
  4. 携带公共请求参数
  5. 进度条
    nprogress
发送请求的流程：
  1.request() 
  2.请求拦截
  3.发送请求
  4.响应拦截
  5.await后面的代码

响应逻辑
- 成功回调，响应成功（200-299）
 - 判断code是200 功能成功 返回成功的数据  response.data.data
 - code不是200，功能失败 进行失败的错误提示  response.data.message  并返回失败的promise
- 失败回调，响应失败 （非2XX）
  - 服务器有响应结果，但是失败
    - 提示错误信息根据对应的响应状态码
      - err.response.status  响应状态码
        404 
        401
        407
        403
  - 服务器没有响应结果
    - 提示失败原因 对应的err.message
      - err.message 失败原因
          请求超时 timeout of 1ms exceeded
          断网 Network Error
  - 返回失败的promise
*/
